package com.link.base.base.approval.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.link.base.base.approval.dao.mybatis.mapper.ApprovalMapper;
import com.link.base.base.approval.dao.mybatis.mapper.ApprovalNodeMapper;
import com.link.base.base.approval.model.Approval;
import com.link.base.base.approval.model.ApprovalNode;
import com.link.core.basic.dao.mapper.BasicMapper;
import com.link.core.basic.service.BasicServiceException;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.basic.service.ServiceException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * Copyright  hande Company. All Rights Reserved.
 *
 * @author unknwon
 * @date: 2018/11/8 14:42
 * @version 1.0
 */

@Service
public class ApprovalServiceImpl extends BasicServiceImpl<Approval> implements ApprovalService {

    @Resource
    private ApprovalMapper approvalMapper;

    @Resource
    private ApprovalNodeMapper approvalNodeMapper;

    @Override
    public BasicMapper<Approval> getBasicMapper() throws Exception {
        return approvalMapper;
    }

    /**
     * 匹配一个审批流程
     * @param type
     * @return Approval
     * @throws Exception
     */
    @Override
    public Approval decideApproval(String type) throws Exception {
        if (type == null || "".equals(type)) {
            throw new ServiceException("APPROVAL-011");
        }
        String statusValue = "Active";
        Approval entity = new Approval();
        entity.setApprovalFlowType(type);
        entity.setStatus(statusValue);
        entity.setPageFlag(false);
        List<Approval> list = approvalMapper.queryByExamplePage(entity);
        if (list == null || list.size() == 0) {
            throw new ServiceException("APPROVAL-012");
        } else if (list.size() > 1) {
            throw new ServiceException("APPROVAL-013");
        }
        return list.get(0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void subdataUpdate(Approval entity) throws Exception {
        if (entity.getId() == null) {
            throw new BasicServiceException("当前更新记录的rowId不能为空");
        }
        this.update(entity);
        // 解析实体的flowJson字段
        if (entity.getFlowJson() != null && !"".equals(entity.getFlowJson())) {
            String flowJson = entity.getFlowJson();
            JSONObject jsonObject = JSONObject.parseObject(flowJson);
            JSONArray nodesArray = jsonObject.getJSONArray("nodes");
            JSONArray edgesArray = jsonObject.getJSONArray("edges");
            Map<String, ApprovalNode> nodeMap = new HashMap<>();
            // 存储条件节点列表
            JSONArray conditionArray = new JSONArray();
            // 解析node节点，将其中流程节点取出转换成审批节点实体,并将开始的审批节点Id和结束的节点Id取出
            for (int i = 0; nodesArray != null && i < nodesArray.size(); i++) {
                JSONObject nodeObject = nodesArray.getJSONObject(i);
                if (nodeObject == null || nodeObject.getString("shape") == null) {
                    throw new BasicServiceException("当前画布没有保存任何节点或者节点参数有误");
                }
                if ("rect".equals(nodeObject.getString("shape"))) {
                    ApprovalNode approvalNode = (JSON.parseObject(nodeObject.getString("data"), ApprovalNode.class));
                    nodeMap.put(nodeObject.getString("id"), approvalNode);
                }
                if ("diamond".equals(nodeObject.getString("shape"))) {
                    conditionArray.add(nodeObject.getJSONObject("data"));
                }
            }
            // 给当前nodeMap设置节点顺序(序号改为前端传，所以暂时取消该方法) nodeSetSn(Arrays.asList(startId), nodeMap, edgesArray, startId, endId, 1);

            // 给当前nodeMap中设置条件节点的条件字段
            if (conditionArray.size() > 0) {
                nodeSetCondition(nodeMap, edgesArray, conditionArray);
            }

            // 查询当前审批流程的所有节点
            List<ApprovalNode> nodes = approvalNodeMapper.queryNodesByApprovalId(entity.getId());
            // 待删除节点
            List<Long> deleteIds = new ArrayList<>();
            // 已更新节点
            List<String> updateIds = new ArrayList<>();
            for (ApprovalNode item : nodes) {
                boolean deleteFlag = true;
                for (String id : nodeMap.keySet()) {
                    if (item.getId() == Long.parseLong(id)) {
                        approvalNodeMapper.update(nodeMap.get(id));
                        deleteFlag = false;
                        updateIds.add(id);
                        break;
                    }
                }
                if (deleteFlag) {
                    deleteIds.add(item.getId());
                }
            }

            // 批量删除节点
            if (deleteIds.size() > 0) {
                approvalNodeMapper.deleteByIds(deleteIds);
            }
        }
    }

    /**
    * 设置nodeMap中存在条件节点
    * @author Lizongting
    * @date 2020/6/13
    */
    private void nodeSetCondition(Map<String, ApprovalNode> nodeMap, JSONArray edgesArray, JSONArray conditionArray) {
        for ( int i = 0;  i < conditionArray.size(); i++) {
            JSONObject conditionObject = conditionArray.getJSONObject(i);
            for (int j = 0; j < edgesArray.size(); j++) {
                JSONObject edgeObject = edgesArray.getJSONObject(j);
                if (edgeObject.getString("target").equals(conditionObject.getString("id"))) {
                    String sourceId = edgeObject.getString("source");
                    if (nodeMap.get(sourceId) != null && conditionObject.getString("decideFunId") != null) {
                        nodeMap.get(sourceId).setDecideFunId(Long.parseLong(conditionObject.getString("decideFunId")));
                    }
                    break;
                }
            }
        }
    }

    /**
     * 递归遍历当前的nodeMap，给每个节点设置当前节点的序号以及下节点(或者下个拒绝节点的序号)
     * @author Lizongting
     * @date 2020/6/10
     */
    public void nodeSetSn(List<String> ids, Map<String, ApprovalNode> nodeMap,
                          JSONArray edgesArray, String startId, String endId, int order) throws Exception {
        // 初始化递归性需要的id
        List<String> nextIds = new ArrayList<>();
        //遍历传入的id，更新id所在的节点对应的次序
        for (int i = 0; ids != null && i < ids.size(); i++) {
            for (int j = 0; edgesArray != null && j < edgesArray.size(); j++) {
                JSONObject jsonObject = edgesArray.getJSONObject(j);
                // 当前节点Id和下一节点Id
                String sourceId = jsonObject.getString("source");
                String targetId = jsonObject.getString("target");
                if (jsonObject == null || sourceId == null || targetId == null) {
                    throw new BasicServiceException("当前画布没有保存edges节点或者edges节点参数有误");
                }
                // 如果是初始节点，设置target对应的流程节点的序号为 1
                if (startId.equals(ids.get(i))) {
                    nextIds.add(targetId);
                    if (nodeMap.get(targetId) != null) {
                        nodeMap.get(targetId).setSn("1");
                        order++;
                    }
                    break;
                } else if (!endId.equals(ids.get(i))) {
                    // 传入Id如果是endId,则找不到后续节点，无需任何设置
                    // 再判断找到的targetId是不是endId,如果是则设置当前传入Id对应的节点nextSN为null
                    if (endId.equals(targetId)) {
                        if (nodeMap.get(sourceId) != null) {
                            nodeMap.get(sourceId).setNextSN(null);
                        }
                    } else {
                        // 如果传入Id匹配到了sourceId，则设置当前Id对应的节点nextSN或者refuseSN为order，后续节点sn为order
                        if (ids.get(i).equals(sourceId)) {
                            // 如果targetId匹配到的节点是已经赋值了sn的节点,则nextSn设置为已经有的值，且order不会自增
                            String orderString = String.valueOf(order);
                            if (nodeMap.get(targetId) != null && nodeMap.get(targetId).getSn() != null) {
                                orderString = nodeMap.get(targetId).getSn();
                            } else if (nodeMap.get(targetId) != null) {
                                nodeMap.get(targetId).setSn(String.valueOf(order));
                            }
                            if (nodeMap.get(sourceId) != null) {
                                nodeMap.get(sourceId).setNextSN(orderString);
                            }
                            if (!nextIds.contains(targetId) || !orderString.equals(String.valueOf(order))) {
                                nextIds.add(targetId);
                                order++;
                            }
                        }
                    }
                }
            }
        }
        if (nextIds.size() > 0 && order < 100) {
            nodeSetSn(nextIds, nodeMap, edgesArray, "0", endId, order);
        }
    }

}
